package com.yiban.demo.Global.AOP.Aspect;

import com.yiban.demo.Global.AOP.Annotation.CheckList;
import com.yiban.demo.Global.Exception.MethodArgumentNotLegalException;
import com.yiban.demo.Global.Controller.ExceptionControllerAdvice;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.MethodArgumentNotValidException;

import javax.servlet.http.HttpServletResponse;
import javax.validation.ConstraintViolation;
import javax.validation.Validator;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;


@Component
@Aspect
public class CheckListAspect {
    @Autowired
    private Validator validator;

    /**
     * 切点为所有标识 {@code com.yiban.demo.Global.AOP.Annotation.CheckList}注解的类
     */
    @Pointcut(value = "@annotation(com.yiban.demo.Global.AOP.Annotation.CheckList)")
    public void checkListPointCut(){}
    @Before("checkListPointCut()")
    public void checkListInController(JoinPoint joinPoint){
        //获取切点信息
        Method method = ((MethodSignature) joinPoint.getSignature())
                .getMethod();
        CheckList checkListAnnotation = method.getAnnotation(CheckList.class);
        Class<?> group = checkListAnnotation.group();
        int listPos = checkListAnnotation.ListPos();

        if (listPos>=joinPoint.getArgs().length){
            throw new ArrayIndexOutOfBoundsException(method.getName()+"的CheckList注解ListPos参数越界");
        }

        List uncheckList = (List) joinPoint.getArgs()[listPos];
        final ArrayList<String> errorMessages = new ArrayList<>();
        for (int i = 0; i < uncheckList.size(); i++) {
            //反正会回收，又没延长变量存活
            //我没开逃逸分析，这个还是存活在堆上
            final StringBuilder msg = new StringBuilder("第");
            msg.append(i+1).append("个").append(":");
            //获取单个错误信息，未开启fast-fail模式，会找出全部错误
            Set<ConstraintViolation<Object>> validate = validator.validate(uncheckList.get(i), group);
            if (!validate.isEmpty()) {
                msg.append(
                        //转化为实体类中的属性校验错误信息提示
                        validate.stream()
                                .map(ConstraintViolation::getMessage)
                                .collect(Collectors.joining(" "))
                );
                errorMessages.add(msg.toString());
            }
        }
        /**不为空抛出异常，会有
         * {@link ExceptionControllerAdvice#bindExceptionHandle(MethodArgumentNotValidException, HttpServletResponse)}
         * 处理
         */
        if (!errorMessages.isEmpty()) {
            throw new MethodArgumentNotLegalException(String.join("\n", errorMessages));
        }


    }
}
